
/**
 * webview page 主要对象
 * 主要负责网页与外部app层双向通信
 * 内部还有一些常用工具api以及窗口控制api
 * @author xueyanjun
 * @since 2023-01-06
 */
class XwBridge {
	
	constructor(){
		this._init();
	}
	
	_init(){
		const _this = this;
		_this.isReady = false;
		_this.callbackPool = new Map();
		_this._plugins = [];
		_this._readyListeners = [];
		_this._enableAppMessageListen();
		_this.$plus = null;
		_this.$utils = {
			readFileAsDataURL({path}){
				return this.readFile({path,format:'dataURL'});
			},
			readFileAsText({path}){
				return this.readFile({path,format:'text'});
			},
			readFile({path,format}){
				return new Promise((resolve,reject)=>{
					_this.$plus.io.resolveLocalFileSystemURL(path, (fileEntry) => {
						fileEntry.file((f) => {
							// 通过filereader将图片读取成base64
							var fileReader = new _this.$plus.io.FileReader();
							if(format === 'dataURL'){
								fileReader.readAsDataURL(f);
							}else if(format === 'text'){
								fileReader.readAsText(f);
							}
							fileReader.onloadend = (evt)=> {
								resolve({data:evt.target.result});
							}
							fileReader.onerror = (err)=>{
								reject(err);
							}
						});
					});
				});
			},
			setStatusBarColor({fontStyle,backgroudColor}){
				// 应用是沉浸式状态栏时设置无效
				if(fontStyle){ // light or dark
					_this.$plus.navigator.setStatusBarStyle(fontStyle);
				}
				if(backgroudColor){ // 16进制颜色值
					_this.$plus.navigator.setStatusBarBackground(backgroudColor);
				}
			},
			setNavBar(styleOptions){
				_this.$plus.webview.currentWebview().setStyle({titleNView:styleOptions});
			}
		}
		
		document.addEventListener('plusready', function(){
			// plus 对象注入完成
			_this.isReady = true;
			_this.$plus = plus;
			_this._replacePostMsgFunction();
			
			// 覆盖浏览器部分函数和对象
			window.XMLHttpRequest = _this.$plus.net.XMLHttpRequest;
			window.close = ()=>{
				_this.windowClose();
			}
			window.hide = ()=>{
				_this.windowHide();
			}
			window.history.back = ()=>{
				_this.windowHistoryBack();
			}
			
			// 调用注册的ready函数
			if(_this._readyListeners.length > 0){
				_this._readyListeners.forEach(fn=>{
					fn(_this);
				})
				_this._readyListeners = null;
			}
		}, false);
	}
	
	_enableAppMessageListen(){
		this._onMsg = function(msg){
			const action = msg.a;
			const data = msg.d;
			if(action === 'cus'){
				this.callbackPool.forEach((v,k)=>{
					v(data);
				});
				return;
			}
			if(action === 'event'){
				if(data.me === 'loadURL'){
					if(this.onXwLoadURL){
						this.onXwLoadURL(data.location)
					}
				}
				return;
			}
			for (let plugin of this._plugins) {
				if(plugin && plugin.$bridgeMessageTypes.includes(action)){
					Promise.resolve().then(()=>{
						plugin.$onBridgeMessage(data);
					});
					return;
				}
			}
		}
	}
	
	_replacePostMsgFunction(){
		if(!this.$plus.webview.postMessageToUniNView){
			return;
		}
		const _this = this;
		const postMsgToUniApp = (message)=>{
			_this.$plus.webview.postMessageToUniNView({  
				type: 'xw-msg',
				xwId: window.__WebVieW_Id__,
				args: message
			}, '__uniapp__service');
		}
		const replacePostMsgFunction = ()=>{
			if(_this._postMsg !== postMsgToUniApp){
				_this._postMsg = postMsgToUniApp;
			}
		}
		_this._postMsg = function(msg){
			// 首次使用有部分环境可能没有初始化完成，异常后在下一轮事件循环执行(宏任务)
			try{
				postMsgToUniApp(msg);
				replacePostMsgFunction();
			}catch(err){
				setTimeout(() => {
					replacePostMsgFunction();
					postMsgToUniApp(msg);
				});
			}
		}
	}
	
	_postMsg(msg) {
		// 默认发送消息方式仍然可以使用，但使用过程中发现了会破坏开发环境时的网页热更新机制
		// 所以在网页loaded事件完成后，主动调用_replacePostMsgFunction函数，条件允许的情况下会替换默认实现
		const p = JSON.stringify(msg);
		window.open('k://xe?p='+encodeURIComponent(p));
	}
	
	ready(fn){
		if(this.isReady){
			fn(this);
		}else{
			this._readyListeners.push(fn);
		}
	}
	
	onMessage(fn) {
		if(fn == null || typeof fn !== 'function'){
			throw new Error('注册webview native消息监听参数必须是一个function');
		}
		if(fn.xwfnId){
			return;
		}
		fn.xwfnId = 'cb-' + this.genNonDuplicateId();
		this.callbackPool.set(fn.xwfnId,fn);
	}
	
	offMessage(fn){
		if(fn == null || typeof fn !== 'function'){
			throw new Error('移除webview native消息监听参数必须是一个function');
		}
		this.callbackPool.forEach((v,k)=>{
			if(v === fn || v.xwfnId === fn.xwfnId){
				this.callbackPool.delete(v.xwfnId);
				fn.xwfnId = void 0;
			}
		});
	}
	
	onXwLoadURL(location){
		window.location.replace(location.href);
	}
	
	postMessage(msg) {
		this._postMsg({a:'cus',d:msg});
	}
	
	windowHistoryBack(options){
		if(this.isReady){
			//options = {ani,duration,topAct}
			const param = {
				a:'wvapi',
				d:{
					me:'back'
				}
			}
			param.d = Object.assign(param.d,options);
			this._postMsg(param);
		}else{
			window.history.back();
		}
	}
	
	windowClose(options){
		if(this.isReady){
			//options = {ani,duration}
			const param = {
				a:'wvapi',
				d:{
					me:'close'
				}
			}
			param.d = Object.assign(param.d,options)
			this._postMsg(param);
		}else{
			window.close();
		}
	}
	
	windowHide(options){
		if(this.isReady){
			//options = {ani,duration}
			const param = {
				a:'wvapi',
				d:{
					me:'hide'
				}
			}
			param.d = Object.assign(param.d,options)
			this._postMsg(param);
		}else{
			console.error('当前浏览器不支持隐藏')
		}
	}
	
	genNonDuplicateId(){
		return Number(Math.random().toString().substr(2, 8)).toString(36) + Date.now().toString(36);
	}

	/**
	 * 安装扩展
	 */
	installPlugins(...plugins){
		for (let i = 0; i < plugins.length; i++) {
			const plugin = plugins[i];
			if(plugin.$onBridgeInstall == null || !(typeof plugin.$onBridgeInstall === 'function')){
				throw new Error('插件的$onBridgeInstall属性不能为空且必须是一个function');
			}
			plugin.$onBridgeInstall(this);
			if(plugin.$onBridgeMessage == null || !(typeof plugin.$onBridgeMessage === 'function')){
				throw new Error('插件的$onBridgeMessage属性不能为空且必须是一个function');
			}
			if(plugin.$bridgeMessageTypes == null || plugin.$bridgeMessageTypes.length === 0){
				throw new Error('插件的$bridgeMessageTypes属性不能为空且必须是一个array');
			}
			this._plugins.push(plugin);
		}
	}
	
}


const $xwbridge = new XwBridge();
window.xwbridge = $xwbridge;

export default $xwbridge;
